package com.caimiao.miao.library.bluetooth;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;

import com.caimiao.miao.library.bluetooth.state.BtConnectState;
import com.caimiao.miao.library.bluetooth.state.BtServiceState;
import com.caimiao.miao.library.bluetooth.state.BtWriteState;
import com.caimiao.miao.library.bluetooth.state.IBtState;
import com.caimiao.miao.library.utils.tip.LogUtil;
import com.caimiao.miao.library.utils.tip.PrintLog;
import com.caimiao.miao.library.utils.tip.ToastUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

public class BluetoothBLEInterface extends BluetoothGattCallback {
    private final String TAG = "Bluetooth";
    private static String uuidService = "0000fee9-0000-1000-8000-00805f9b34fb";
    private static String uuidCharWrite = "d44bc439-abfd-45a2-b575-925416129600";
    private static final String uuidDes = "00002902-0000-1000-8000-00805f9b34fb";

    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothGatt mBluetoothGatt;
    private boolean isScanning;

    private int retry;
    private Disposable mRetryDisposable;

    private BluetoothGattCharacteristic mWriteCharacteristic;
    private BluetoothGattCharacteristic mNotifyCharacteristic;
    private boolean mNotifyEnabled = false;
    private final int mServerBufferSize = 20;//BLE蓝牙每次发送最多20个字节

    private ObservableEmitter mReadEmitter;
    private ObservableEmitter mStateEmitter;

    public BluetoothBLEInterface(Context context) {
        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager != null) {
            mBluetoothAdapter = bluetoothManager.getAdapter();
        } else {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
    }


    /**
     * 确认是否有蓝牙功能
     */
    public boolean checkHasBluetooth() {
        return mBluetoothAdapter != null;
    }

    /**
     * 确认是否开启蓝牙
     */
    public boolean checkOpenBluetooth() {
        if (mBluetoothAdapter != null)
            return mBluetoothAdapter.isEnabled();
        return false;
    }

    /**
     * 开启蓝牙
     *
     * @return
     */
    public boolean enableBluetooth() {
        return mBluetoothAdapter.enable();
    }

    /**
     * 向用户请求开启蓝牙（Activity）
     */
    public void requestOpenBluetooth(Context context) {
        if (mBluetoothAdapter != null) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            ((Activity) context).startActivityForResult(intent, IBluetoothCode.REQUEST_ENABLE_BT);
        }
    }

    public boolean isEnableBluetooth() {
        return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
    }

    /**
     * 获取已配对的蓝牙设备
     */
    public List<BTDevice> getBondedDevices() {
        List<BTDevice> devices = new ArrayList<>();
        if (mBluetoothAdapter != null) {
            Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
            for (BluetoothDevice device : bondedDevices) {
                devices.add(new BTDevice(device));
            }
        }
        return devices;
    }

    /**
     * 启动蓝牙搜索
     */
    public boolean startScan(final BluetoothAdapter.LeScanCallback mScanCb) {
        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
                && !mBluetoothAdapter.isDiscovering() && !isScanning) {
            isScanning = true;
            mBluetoothAdapter.startLeScan(mScanCb);
            Observable.timer(15, TimeUnit.SECONDS)
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) throws Exception {
                            stopScan(mScanCb);
                        }
                    });

            return true;
        }
        return false;
    }

    /**
     * 停止蓝牙搜索
     *
     * @param mScanCb
     */
    public void stopScan(BluetoothAdapter.LeScanCallback mScanCb) {
        isScanning = false;
        mBluetoothAdapter.stopLeScan(mScanCb);
    }

    /**
     * 连接蓝牙
     *
     * @param context
     * @param address
     */
    public void connect(Context context, String address) {
        String macAddress = address;
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
        //连接操作一定要在主线程
        mBluetoothGatt = device.connectGatt(context, false, this);
        if (mBluetoothGatt == null) {
            ToastUtil.show("蓝牙连接失败！！！");
        } else {
            retry = 0;
            ToastUtil.show("正在连接蓝牙...");
        }
    }

    /**
     * 重新连接蓝牙
     */
    public void reconnect() {
        //多次连接保证连接的成功率，适宜次数为3次，因为连接3次都不成功的情况，一般需要重启蓝牙或手机且等待时间适宜
        if (mBluetoothGatt != null) {
            retry++;
            if (retry > 3) {
                ToastUtil.show("蓝牙连接失败！！！");
                return;
            }
            boolean state = mBluetoothGatt.connect();
            if (!state) {
                mRetryDisposable = Observable
                        .timer(1, TimeUnit.SECONDS)
                        .subscribe(aLong -> reconnect());
            } else {
                if (mRetryDisposable != null)
                    mRetryDisposable.dispose();
            }
        }
    }

    /**
     * 蓝牙是否连接
     *
     * @return
     */
    public boolean isConnect() {
        return mBluetoothGatt != null;
    }

    /**
     * 关闭蓝牙连接
     */
    public void close() {
        //当连接bluetoothGatt断开连接的时候一定要close调，释放系统资源，因为一般的手机最多能维持6个gatt连接
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        reset();
    }

    /**
     * 写数据
     */
    public boolean write(byte[] value) {
        byte[] bytes = value;
        boolean ret = false;
        if (mBluetoothGatt == null) {
            LogUtil.e(TAG, "BluetoothAdapter not initialized !");
            return ret;
        }

        if (bytes == null) {
            LogUtil.e(TAG, "bytes = null !");
            return ret;
        }

        int length = bytes.length;
        if (length <= mServerBufferSize) {
            return writeValue(bytes);
        } else {
            int count;
            int offset = 0;
            while (offset < length) {
                //分包发送
                if ((length - offset) < mServerBufferSize)
                    count = length - offset;
                else
                    count = mServerBufferSize;
                byte tempArray[] = new byte[count];
                System.arraycopy(bytes, offset, tempArray, 0, count);
                ret = writeValue(tempArray);
                if (!ret) return ret;//如果发送失败，则全部失败
                offset = offset + count;
            }
        }
        return ret;
    }


    /**
     * 读数据（BLE一般使用notify返回数据，而不使用read主动读取）
     */
    public boolean read() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null || mWriteCharacteristic == null) {
            LogUtil.e(TAG, "BluetoothAdapter mBluetoothGatt and mWriteCharacteristic are not initialized");
            return false;
        }
        LogUtil.e(TAG, "read readCharacteristic");
        return mBluetoothGatt.readCharacteristic(mWriteCharacteristic);
    }

    /**
     * 蓝牙状态返回<state>
     *
     * @param emitter
     */
    public void setStateObservableEmitter(ObservableEmitter<IBtState> emitter) {
        mStateEmitter = emitter;
    }

    /**
     * 读监听<byte[]>
     *
     * @param emitter
     */
    public void setReadObservableEmitter(ObservableEmitter<byte[]> emitter) {
        mReadEmitter = emitter;
    }

    private boolean writeValue(byte[] bytes) {
        if (mBluetoothGatt == null) {
            LogUtil.e(TAG, "BluetoothAdapter not initialized");
            return false;
        }
        if (mWriteCharacteristic == null) {
            LogUtil.e(TAG, "mWriteCharacteristic not initialized");
            return false;
        }
        mWriteCharacteristic.setValue(bytes);
        return mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
    }

    private void updateValueForNotification(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic characteristic) {
        if (bluetoothGatt == null || characteristic == null) {
            LogUtil.e(TAG, "invalid arguments");
            return;
        }
        final byte[] data = characteristic.getValue();
        LogUtil.e(TAG, "updateValueForNotification, value: " + data);
        if (data != null && data.length > 0) {
            //读取蓝牙信息强度
            mBluetoothGatt.readRemoteRssi();
            if (mReadEmitter != null) mReadEmitter.onNext(data);
        }
    }

    private void reset() {
        mWriteCharacteristic = null;

        mNotifyCharacteristic = null;
        mNotifyEnabled = false;
    }

    private boolean setNotify(BluetoothGatt bluetoothGatt, boolean EnableNotifyChara) {
        if (mNotifyEnabled) {
            return true;
        }
        return setCharacteristicNotification(bluetoothGatt, mNotifyCharacteristic, EnableNotifyChara);
    }

    private boolean setCharacteristicNotification(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic characteristic, boolean enabled) {
        if (bluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return false;
        }

        bluetoothGatt.setCharacteristicNotification(characteristic, enabled);

        try {
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(uuidDes));
            if (descriptor != null) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                return (bluetoothGatt.writeDescriptor(descriptor));
            } else {
                LogUtil.e(TAG, "descriptor is null");
                return false;
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        ////连接状态改变时回调，连接成功后使用gatt.discoverService()发现连接设备的服务，当断开连接是应使用gatt.close（）释放连接
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            mBluetoothGatt.discoverServices();//连接成功，发现服务
            PrintLog.printBle("connection : 蓝牙连接成功");
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            PrintLog.printBle("connection : 蓝牙连接失败, newState : " + newState);
        }
        if (mStateEmitter != null) mStateEmitter.onNext(new BtConnectState(newState, null));
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        ////获取设备的服务，如果服务获取失败，可认为连接是失败
        if (status == BluetoothGatt.GATT_SUCCESS) {

            reset();
            //获取服务通道
            BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(uuidService));
            if (service == null) {
                PrintLog.printBle("services， 蓝牙服务连接失败， service = " + service);
                return;
            }
            List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
            for (int j = 0; j < gattCharacteristics.size(); j++) {
                BluetoothGattCharacteristic chara = gattCharacteristics.get(j);
                if (chara.getUuid().toString().equals(uuidCharWrite)) {
                    LogUtil.i(TAG, "Write char UUID is : " + chara.getUuid().toString());
                    mWriteCharacteristic = chara;
                } else if (chara.getProperties() == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
                    LogUtil.i(TAG, "Notify Char UUID is : " + chara.getUuid().toString());
                    mNotifyCharacteristic = chara;
                }
            }
            mNotifyEnabled = setCharacteristicNotification(mBluetoothGatt, mNotifyCharacteristic, true);
            PrintLog.printBle("services， 蓝牙服务连接成功");
        } else {
            PrintLog.printBle("services， 蓝牙服务连接失败，status = " + status);
        }
        if (mStateEmitter != null) mStateEmitter.onNext(new BtServiceState(status, null));
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        //写操作回调
//        PrintLog.printBle("onCharacteristicWrite， 写入蓝牙结果" + (status == BluetoothGatt.GATT_SUCCESS ? "成功" : "失败"));
        if (mStateEmitter != null) mStateEmitter.onNext(new BtWriteState(status, null));
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        LogUtil.w(TAG, "onDescriptorWrite");
        setNotify(mBluetoothGatt, true);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        //通知数据更新时回调
        LogUtil.w(TAG, "onCharacteristicChanged");
        updateValueForNotification(gatt, characteristic);
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        //蓝牙信号强度改变回调
        PrintLog.printBle("蓝牙信号强度改变: rssi = " + rssi);
    }

    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        //蓝牙发送、接收的长度改变回调
        LogUtil.w(TAG, "onMtuChanged");
    }
}
